using System;
using UnityEngine;
using UnityEngine.UI;

namespace CGF
{
    [AddComponentMenu("UI/Effects/CurvedText")]
    [RequireComponent(typeof(Text))]
    public class CurvedText : BaseMeshEffect
    {
        public float arc = 12;
        public float radius = 800;
        public float angleOffset = 90;
        public bool alignCenter = true;

        private Matrix4x4 _matrix;
        private Text _text;
        private float charAngle;
        private float totalAngle;
        private float _scaleRatio = 0.005f;

        protected override void OnEnable()
        {
            _text ??= GetComponent<Text>();
            _text.horizontalOverflow = HorizontalWrapMode.Overflow;
            _text.verticalOverflow = VerticalWrapMode.Overflow;
        }

        public override void ModifyMesh(VertexHelper vertexHelper)
        {
            var vertices = GetVertices(vertexHelper);
            totalAngle = 0;
            for (int i = 0; i < vertices.Length; i += 4)
            {
                float charWidth = vertices[i + 1].position.x - vertices[i].position.x;
                totalAngle += charWidth * arc * _scaleRatio;
            }
            float charAverageWidth = totalAngle / (vertices.Length / 4f);

            charAngle = angleOffset;
            if (alignCenter)
            {
                charAngle += (totalAngle * 0.5f - charAverageWidth * 0.5f);
            }
            float preCharWidth = 0, currCharWidth = 0;
            for (int i = 0; i < vertices.Length; i += 4)
            {
                var charIndex = i / 4;
                var charCount = vertices.Length / 4;
                if (charIndex > 0)
                {
                    charAngle -= preCharWidth * 0.5f * arc * _scaleRatio;
                    currCharWidth = vertices[i + 1].position.x - vertices[i].position.x;
                    charAngle -= currCharWidth * 0.5f * arc * _scaleRatio;
                }
                ModifyCharacter(new Span<UIVertex>(vertices, i, 4), charAngle);
                preCharWidth = vertices[i + 1].position.x - vertices[i].position.x;
            }
            for (int i = 0; i < vertices.Length; i++)
            {
                vertexHelper.SetUIVertex(vertices[i], i);
            }
        }

        private void ModifyCharacter(Span<UIVertex> vertices, float angle)
        {
            var horizontalCenter = (vertices[0].position.x + vertices[2].position.x) / 2;
            Vector3 charMidBaselinePos = new Vector2(horizontalCenter, 0);
            vertices[0].position -= charMidBaselinePos;
            vertices[1].position -= charMidBaselinePos;
            vertices[2].position -= charMidBaselinePos;
            vertices[3].position -= charMidBaselinePos;

            var radians = angle * Mathf.Deg2Rad;
            var normal = new Vector3(Mathf.Cos(radians), Mathf.Sin(radians));

            var translation = normal * radius;
            var rotation = Quaternion.LookRotation(Vector3.forward, normal);

            _matrix = Matrix4x4.TRS(translation, rotation, Vector3.one);
            vertices[0].position = _matrix.MultiplyPoint3x4(vertices[0].position);
            vertices[1].position = _matrix.MultiplyPoint3x4(vertices[1].position);
            vertices[2].position = _matrix.MultiplyPoint3x4(vertices[2].position);
            vertices[3].position = _matrix.MultiplyPoint3x4(vertices[3].position);

            vertices[0].position.y -= radius;
            vertices[1].position.y -= radius;
            vertices[2].position.y -= radius;
            vertices[3].position.y -= radius;
        }

        private UIVertex[] GetVertices(VertexHelper vertexHelper)
        {
            var vertexes = new UIVertex[vertexHelper.currentVertCount];
            for (int i = 0; i < vertexes.Length; i++)
            {
                vertexHelper.PopulateUIVertex(ref vertexes[i], i);
            }
            return vertexes;
        }
    }
}